using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using ALib;
using ALib.Misc;
using AxTWSLib;
using TWSLib;
using System.Collections;
using _DTwsEvents_historicalDataEventHandler = AxTWSLib._DTwsEvents_historicalDataEventHandler;

namespace TradingPlatform.IB.Operations
{
    public class MarketData
    {
        //private readonly AxTws _tws;
        private Comm _comm;
        protected TickerNameAndId _tickerId;
        public readonly SortedList<string,ObjectTuple2<UtcConverter,List<string>>> _tickSeriesList;
        public readonly SortedList<string, double[]> _lastTick;

        private readonly FixedQueue<double> _totalVolumeQueue;
        public readonly SortedList<string, double[]> _lastTick_AdjustedVolume;

        private Hashtable _symbolToTicker = new Hashtable();

        private const int _tickItemNumber = 7;

        public MarketData(Comm comm)
        {
            _comm = comm;

            //tws.tickPrice += new AxTWSLib._DTwsEvents_tickPriceEventHandler(tws_tickPrice);
            //tws.tickSize += new AxTWSLib._DTwsEvents_tickSizeEventHandler(tws_tickSize);

            _tickSeriesList = new SortedList<string, ObjectTuple2<UtcConverter, List<string>>>();
            _lastTick = new SortedList<string, double[]>();

            _totalVolumeQueue = new FixedQueue<double>(2);
            _lastTick_AdjustedVolume = new SortedList<string, double[]>();

            _tickerId = new TickerNameAndId();
        }

        public void RequestMarketData(int tradingPlatformTimeZoneIndex, int exchangeTimeZoneIndex,
                                      string exchange, string secType, string symbol, string expiry, string currency, bool snapshot)
        {
            //IContract contract = _tws.createContract();

            //Tws x = new Tws();

            //contract.exchange = exchange;
            //contract.secType = secType;
            //contract.symbol = symbol;
            //contract.expiry = expiry;
            //contract.currency = currency;

            string tickerName = TickerNameAndId.ToTickerName(exchange, secType, symbol, expiry, currency);
            _tickerId.Add(tickerName);

            if (!_symbolToTicker.Contains(symbol))
            {
                _symbolToTicker.Add(symbol, tickerName);
            }

            if (!_tickSeriesList.ContainsKey(tickerName))
            {
                _tickSeriesList.Add(tickerName, new ObjectTuple2<UtcConverter, List<string>>(new UtcConverter(tradingPlatformTimeZoneIndex, exchangeTimeZoneIndex),new List<string>()));
            }

            int tickerId = _tickerId.GetId(tickerName);
            int useSnapshot = snapshot ? 1 : 0;

            _comm.ReqMarketData(symbol, true);

            //_tws.reqMktDataEx(tickerId, contract, "", useSnapshot);
        }

        public void CancelMarketData(string exchange, string secType, string symbol, string expiry, string currency)
        {
            string tickerName = TickerNameAndId.ToTickerName(exchange, secType, symbol, expiry, currency);
            if(_tickerId.Contains(tickerName))
            {

                int tickerId = _tickerId.GetId(tickerName);
                //_tws.cancelMktData(tickerId);
                _comm.ReqMarketData(tickerName, false);
                _tickSeriesList[tickerName].Object1.Clear();
            }
        }

        public void CancelAllMarketData()
        {
            for (int i = 0; i < _tickerId.Count; i++)
            {
                int tickerId = i + 1;
                string name = _tickerId.GetTickerName(tickerId);
                _comm.ReqMarketData(name, false);
                //_tws.cancelMktData(tickerId);   
            }
            _tickSeriesList.Clear();
        }

        public void UpdateBidAsk(MarketDataBestLevelMessage data)
        {
            string symbol = data.SymbolField.StringValue.Trim();
            string tickerName = (String) _symbolToTicker[symbol];

            if (!_lastTick.ContainsKey(tickerName))
            {
                _lastTick.Add(tickerName, new double[_tickItemNumber]);
                for (int i = 0; i < _tickItemNumber; i++)
                {
                    _lastTick[tickerName][i] = double.NaN;
                }
            }
            double bidPx = data.BestBidPxField.DoubleValue;
            double askPx = data.BestAskPxField.DoubleValue;
            int bidSz = data.BestBidSzField.IntValue;
            int askSz = data.BestAskSzField.IntValue;
            _lastTick[tickerName][1] = bidPx;
            _lastTick[tickerName][2] = askPx;
            _lastTick[tickerName][0] = bidSz;
            _lastTick[tickerName][3] = askSz;

            DateTime tradingPlatformTime = DateTime.Now;
            DateTime exchangeTime = _tickSeriesList[tickerName].Object0.TradingPlatformTimeToExchangeTime(tradingPlatformTime);

            string tickString = exchangeTime.ToString(DateTimeFormatType.DateSimple) + "," + exchangeTime.ToString(DateTimeFormatType.TimeComplete) + "," + CollectionToString.ArrayToString(_lastTick[tickerName], ",");

            _tickSeriesList[tickerName].Object1.Add(tickString);
        }

        public void UpdateTraded(MarketDataTradeMessage data)
        {
            string symbol = data.SymbolField.StringValue.Trim();
            string tickerName = (String)_symbolToTicker[symbol];

            if (!_lastTick.ContainsKey(tickerName))
            {
                _lastTick.Add(tickerName, new double[_tickItemNumber]);
                for (int i = 0; i < _tickItemNumber; i++)
                {
                    _lastTick[tickerName][i] = double.NaN;
                }
            }
            double lastPx = data.LastPxField.DoubleValue;
            double bid = _lastTick[tickerName][1];
            double ask = _lastTick[tickerName][2];
            int lastSz = data.LastSzField.IntValue;
            int volume = data.VolumeField.IntValue;

            if (IsGoodPrice(bid) && IsGoodPrice(ask) && IsGoodPrice(lastPx))
            {
                if (lastPx < bid - 0.01 * bid)
                {
                    lastPx = bid;
                }
                if (ask + 0.01 * ask < lastPx)
                {
                    lastPx = ask;
                }
                _lastTick[tickerName][4] = lastPx;
            }
                
            _lastTick[tickerName][5] = lastSz;
            _lastTick[tickerName][6] = volume;

            DateTime tradingPlatformTime = DateTime.Now;
            DateTime exchangeTime = _tickSeriesList[tickerName].Object0.TradingPlatformTimeToExchangeTime(tradingPlatformTime);

            string tickString = exchangeTime.ToString(DateTimeFormatType.DateSimple) + "," + exchangeTime.ToString(DateTimeFormatType.TimeComplete) + "," + CollectionToString.ArrayToString(_lastTick[tickerName], ",");

            _tickSeriesList[tickerName].Object1.Add(tickString);

            _lastTick[tickerName][5] = 0;

        }

        //void tws_tickPrice(object sender, _DTwsEvents_tickPriceEvent e)
        //{
        //    int tickerId = e.id;
        //    string tickerName = _tickerId.GetTickerName(tickerId);
        //    int index = e.tickType;
        //    if (index ==1||index==2||index==4)
        //    {
        //        if (!_lastTick.ContainsKey(tickerName))
        //        {
        //            _lastTick.Add(tickerName, new double[_tickItemNumber]);
        //            for (int i = 0; i < _tickItemNumber; i++)
        //            {
        //                _lastTick[tickerName][i] = double.NaN;
        //            }
        //        }

        //        if (true)
        //        {
        //            // use bid and ask to limit last price, to avoid error data.
        //            if (index == 4)
        //            {
        //                double bid = _lastTick[tickerName][1];
        //                double ask = _lastTick[tickerName][2];
        //                double last = e.price;

        //                if (IsGoodPrice(bid) && IsGoodPrice(ask) && IsGoodPrice(last))
        //                {
        //                    if (last < bid - 0.01 * bid)
        //                    {
        //                        last = bid;
        //                    }
        //                    if (ask + 0.01 * ask < last)
        //                    {
        //                        last = ask;
        //                    }
        //                    _lastTick[tickerName][index] = last;
        //                }
        //            }
        //            else
        //            {
        //                _lastTick[tickerName][index] = e.price;
        //            }
        //        }

                
        //        DateTime tradingPlatformTime = DateTime.Now;
        //        DateTime exchangeTime = _tickSeriesList[tickerName].Object0.TradingPlatformTimeToExchangeTime(tradingPlatformTime);

        //        string tickString = exchangeTime.ToString(DateTimeFormatType.DateSimple) + "," + exchangeTime.ToString(DateTimeFormatType.TimeComplete) + "," + CollectionToString.ArrayToString(_lastTick[tickerName], ",");

        //        _tickSeriesList[tickerName].Object1.Add(tickString);
        //    }
        //}

        static bool IsGoodPrice(double price)
        {
            bool result = !double.IsInfinity(price) && !double.IsNaN(price) && price > 0;
            return result;
        }

        //void tws_tickSize(object sender, _DTwsEvents_tickSizeEvent e)
        //{
        //    int tickerId = e.id;
        //    string tickerName = _tickerId.GetTickerName(tickerId);
        //    int index = e.tickType;
        //    if (index == 0 || index == 3 || index == 5 || index == 8)
        //    {
        //        if (!_lastTick.ContainsKey(tickerName))
        //        {
        //            _lastTick.Add(tickerName, new double[_tickItemNumber]);
        //            for (int i = 0; i < _tickItemNumber; i++)
        //            {
        //                _lastTick[tickerName][i] = double.NaN;
        //            }
        //        }
        //        int tickItemIndex = index == 8 ? 6 : index;
        //        _lastTick[tickerName][tickItemIndex] = e.size;
        //        DateTime tradingPlatformTime = DateTime.Now;
        //        DateTime exchangeTime = _tickSeriesList[tickerName].Object0.TradingPlatformTimeToExchangeTime(tradingPlatformTime);

        //        string tickString = exchangeTime.ToString(DateTimeFormatType.DateSimple) + "," + exchangeTime.ToString(DateTimeFormatType.TimeComplete) + "," + CollectionToString.ArrayToString(_lastTick[tickerName], ",");

        //        _tickSeriesList[tickerName].Object1.Add(tickString);
        //        _lastTick[tickerName][5] = 0;
        //    }
        //}

        public void Clear()
        {
            foreach (KeyValuePair<string, ObjectTuple2<UtcConverter, List<string>>> tickerName_Series in _tickSeriesList)
            {
                tickerName_Series.Value.Object1.Clear();
            }
        }
    }
}